package demo2

import "fmt"

/* demo1 */
type innerS struct {
	in1 int
	in2 int
}

// 结构体可以包含一个或多个匿名（或内嵌）字段，即这些字段没有显式的名字，
// 只有字段的类型是必须的，此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型，即结构体可以包含内嵌结构体。
type outerS struct {
	b      int
	c      float32
	int    // 匿名字段，类型是必须的, 类型名就是字段名
	innerS // 匿名字段，类型是必须的, 类型名就是字段名
}

/* demo2 */
type testA struct {
	ax, ay int
}

type testB struct {
	testA
	bx, by float32
}

/* demo3 */
type obj1 struct {
	name string // name 1 为 string
	age  int
}

type obj2 struct {
	country string
	name    int // name 2 为 int
}

type obj3 struct {
	obj1
	title string
	id    int
	obj2
}

// TestDemo215 类型内嵌和结构体内嵌, 类似于其他语言的继承
func TestDemo215() {
	/*
		demo1: 在一个结构体中对于每一种数据类型只能有一个匿名字段。
	*/
	outer := new(outerS)

	outer.b = 6
	outer.c = 7.5
	outer.int = 60
	outer.in1 = 5
	outer.in2 = 10
	fmt.Println(outer)

	// 使用结构体字面量
	outer2 := outerS{6, 7.5, 60, innerS{5, 10}}
	fmt.Println("outer is: ", outer2)

	/*
		demo2: 内嵌结构体
	*/
	b := testB{testA{1, 2}, 3.0, 4.0}
	fmt.Println(b.ax, b.ay, b.bx, b.by)
	fmt.Println(b.testA)

	/*
		demo3: 结构内嵌特性
			1. 内嵌的结构体可以直接访问其成员变量
				如果结构体有多层嵌入结构体，结构体实例访问任意一级的嵌入结构体成员时都只用给出字段名，
				而无须像传统结构体字段一样，通过一层层的结构体字段访问到最终的字段。例如，ins.a.b.c的访问可以简化为ins.c。
			2. 内嵌结构体的字段名是它的类型名
				内嵌结构体字段仍然可以使用详细的字段进行一层层访问，内嵌结构体的字段名就是它的类型名，
			3. 一个结构体只能嵌入一个同类型的成员，无须担心结构体重名和错误赋值的情况，编译器在发现可能的赋值歧义时会报错。
	*/
	var obj obj3

	obj.id = 1
	obj.country = "China"
	obj.title = "test"
	obj.age = 79
	// 内嵌结构体的嵌入类型中字段重复时，会报错歧义，这时需要通过 structName.fieldname 来修改字段值
	obj.obj1.name = "string name"
	obj.obj2.name = 1213456

	fmt.Println(obj)
}
